package com.hmall.trade.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.CartClient;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.constants.MqConstants;
import com.hmall.common.domain.MultiDelayMessage;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.CollUtils;
import com.hmall.common.utils.UserContext;
import com.hmall.trade.constants.TradeMqConstants;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itheima
 * @since 2023-05-05
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    private final ItemClient itemService;
    private final IOrderDetailService detailService;
    private final CartClient cartService;
    private final RabbitTemplate rabbitTemplate;
    private final CartClient cartClient;
    private final ItemClient itemClient;

    @Override
//    @Transactional
    @GlobalTransactional//  开启全局事务,设计到多个微服务多数据库操作需要全局事务
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.订单数据
        Order order = new Order();
        // 1.1.查询商品
//        List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
        List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
        // 1.2.获取商品id和数量的Map
        Map<Long, Integer> itemNumMap = detailDTOS.stream()
                .collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
        Set<Long> itemIds = itemNumMap.keySet();
        // 1.3.查询商品
        List<ItemDTO> items = itemService.queryItemByIds(itemIds);
        if (items == null || items.size() < itemIds.size()) {
            throw new BadRequestException("商品不存在");
        }
        // 1.4.基于商品价格、购买数量计算商品总价：totalFee
        int total = 0;
        for (ItemDTO item : items) {
            total += item.getPrice() * itemNumMap.get(item.getId());
        }
        order.setTotalFee(total);
        // 1.5.其它属性
        order.setPaymentType(orderFormDTO.getPaymentType());
        order.setUserId(UserContext.getUser());
        order.setStatus(1);
        // 1.6.将Order写入数据库order表中
        save(order);

        // 2.保存订单详情
        List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
        detailService.saveBatch(details);

        // 3.清理购物车商品
//        cartClient.deleteCartItemByIds(itemIds);
        //通过MQ  发送消息来实现购物车商品删除
        rabbitTemplate.convertAndSend(MqConstants.TRADE_EXCHANGE_NAME, MqConstants.ROUTING_KEY_ORDER_CREATE, itemIds, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
//通过消息头传递当前操作的用户
                message.getMessageProperties().setHeader("user-info",UserContext.getUser());
                return message;
            }
        });

//        int i = 1/0;

        // 4.扣减库存
        try {
            itemService.deductStock(detailDTOS);
        } catch (Exception e) {
            throw new RuntimeException("库存不足！");
        }
//        //发送延迟消息用来确保订单支付状态的可靠性
//        rabbitTemplate.convertAndSend(TradeMqConstants.DELAY_EXCHANGE, TradeMqConstants.DELAY_ORDER_ROUTING_KEY, order.getId(), new MessagePostProcessor() {
//            @Override
//            public Message postProcessMessage(Message message) throws AmqpException {  //设置延迟时间
//                Integer delay = 5000;
//                message.getMessageProperties().setDelay(delay);
//                return message;
//            }
//        });

        // 发送延迟消息，用来确保订单支付状态的可靠性优化版
// --- 这是修改的重点区域 ---

// 1. 定义我们的阶梯延迟时间表 (10秒, 1分钟, 30分钟)
//        List<Integer> delayMillis = Arrays.asList(10000, 60000, 1800000);
        List<Integer> delayMillis = Arrays.asList(5000,10000);

// 2. 创建一个 "智能备忘录" (MultiDelayMessage)
//    把订单ID和完整的时间表都放进去
        MultiDelayMessage<Long> multiDelayMessage = new MultiDelayMessage<>(order.getId(), delayMillis);

// 3. 发送这个 "智能备忘录"
        //  在发送前记录日志
        log.info("准备发送订单支付状态检查的延迟消息。订单ID: {}, 延迟计划(ms): {}",
                multiDelayMessage.getData(),
                multiDelayMessage.getDelayMillis());
        rabbitTemplate.convertAndSend(
                TradeMqConstants.DELAY_EXCHANGE,
                TradeMqConstants.DELAY_ORDER_ROUTING_KEY,
                multiDelayMessage, // <== 看这里！我们发送的是整个 MultiDelayMessage 对象
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // 4. 从 "智能备忘录" 中取出第一个延迟时间来设置本次延迟
                        //    注意：这里我们不能移除它，因为如果发送失败，消息需要重试，时间表必须是完整的
                        Integer delay = multiDelayMessage.getDelayMillis().get(0);
                        message.getMessageProperties().setDelay(delay);
                        // 也可以在这里记录具体的发送延迟
                        log.info("初次已经超时开始第下次延迟: {}ms", delay);
                        return message;
                    }
                });
        log.info("订单ID: {} 的延迟消息已成功发送。", order.getId());

        return order.getId();
    }

    @Override
    public void markOrderPaySuccess(Long orderId) {
        Order order = new Order();
        order.setId(orderId);
        order.setStatus(2);
        order.setPayTime(LocalDateTime.now());
        updateById(order);
    }

    /**
     * 取消订单
     *
     * @param orderId
     */
    @Override
    @GlobalTransactional //  开启全局事务
    public void cancelOrder(Long orderId) {
        // 1、更新订单的状态为已关闭
        boolean success = lambdaUpdate()
                .set(Order::getStatus, 5) // 设置订单状态为已关闭
                .set(Order::getCloseTime, LocalDateTime.now())
                .eq(Order::getId, orderId)
                .eq(Order::getStatus, 1) // 关键：只有未付款的订单才能被取消
                .update();

        if (!success) {
            // 如果更新失败，说明订单状态已经不是“未支付”了（可能已被支付或已被取消）
            // 此时直接返回，不再执行后续的库存回退操作
            log.warn("尝试取消订单失败，订单可能已被支付或处理。订单ID: {}", orderId);
            return;
        }
        //2、回退商品库存；商品的购买数在订单详情中
        // 根据订单ID查询订单详情列表
        LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderDetail::getOrderId, orderId);
        List<OrderDetail> orderDetails = detailService.list(queryWrapper);
        // 如果订单详情列表为空，则直接返回
        if (orderDetails == null || CollUtils.isEmpty(orderDetails)) {
            return;
        }
        // 将订单详情列表转换为DTO列表
        List<OrderDetailDTO> orderDetailDTOS = BeanUtils.copyList(orderDetails,
                OrderDetailDTO.class);

        // 将购买数量修改为负数
        for (OrderDetailDTO orderDetailDTO : orderDetailDTOS) {
            orderDetailDTO.setNum(-orderDetailDTO.getNum());
        }
        // 调用库存服务，扣除商品库存
        itemClient.deductStock(orderDetailDTOS);
    }



    /**
     * 构建订单详情
     *
     * @param orderId
     * @param items
     * @param numMap
     * @return
     */
    private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
        List<OrderDetail> details = new ArrayList<>(items.size());
        for (ItemDTO item : items) {
            OrderDetail detail = new OrderDetail();
            detail.setName(item.getName());
            detail.setSpec(item.getSpec());
            detail.setPrice(item.getPrice());
            detail.setNum(numMap.get(item.getId()));
            detail.setItemId(item.getId());
            detail.setImage(item.getImage());
            detail.setOrderId(orderId);
            details.add(detail);
        }
        return details;
    }
}
